﻿using System;
using System.IO;
using System.Drawing;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using COUP.Core.Enums;
using COUP.Core.Files;
using COUP.Core.Entities;

namespace COUP.Core
{
    public class Map : DataMap
    {
        internal int MAX_OBJECTS = 30;
        internal int MAX_DEPTH = 6;
        public bool UpdateDisplay { get; set; }

        public int _mapID;
        private Sector _mapSector;

        public Map(int MapID)
        {
            _mapID = MapID;

            LoadMap();
        }

        public void Insert(iEntity Entity)
        {
            if (_mapSector == null)
                _mapSector = new Sector(new Point(0, 0), new Size(_width, _height), this);

            _mapSector.Insert(Entity);
        }

        public void Remove(iEntity Entity)
        {
            _mapSector.Remove(Entity);
        }

        public List<iEntity> Query(Rectangle Area)
        {
            if (_mapSector == null)
                return new List<iEntity>();

            return _mapSector.Query(Area);
        }

        public iEntity GetEntity(int UniqueID)
        {
            return _mapSector.GetEntity(UniqueID);
        }

        public iEntity GetEntity(string Name)
        {
            return _mapSector.GetEntity(Name);
        }

        public List<Sector> GetSectors(Rectangle Area)
        {
            if (_mapSector == null)
                return new List<Sector>();
            return _mapSector.GetSectors(Area);
        }

        internal void LoadMap()
        {
            FileStream File = null;
            while (!Monitor.TryEnter(Console.Out, 500)) { }
            int CursorPos = Console.CursorLeft;
            int CursorTop = Console.CursorTop;
            Monitor.Exit(Console.Out);

            try
            {
                File = new FileStream(Settings.SmallMap.Replace("{0}", _mapID.ToString()), FileMode.Open);
                while (!Monitor.TryEnter(Console.Out, 500)) { }
                Console.Write(_mapID);
                Console.SetCursorPosition(CursorPos, 0);
                Monitor.Exit(Console.Out);
            }
            catch (Exception e)
            {
                while (!Monitor.TryEnter(Console.Out, 500)) { }
                Console.SetCursorPosition(0, GameMap.ConsoleTop);
                Console.WriteLine("[Game Server] Missing file: {0}", _mapID.ToString() + ".smap");
                Console.SetCursorPosition(CursorPos, CursorTop);
                GameMap.ConsoleTop++;
                Monitor.Exit(Console.Out);
                return;
            }

            using (BinaryReader Reader = new BinaryReader(File))
            {
                int count = 0;

                File.Seek(8, SeekOrigin.Begin);

                _width = Reader.ReadInt32(); _height = Reader.ReadInt32();

                Tiles = new Tile[_width * _height];

                byte Data = Reader.ReadByte();

                for (ushort y = 0; y < _height; y++)
                {
                    for (ushort x = 0; x < _width; x++)
                    {
                        switch (count)
                        {
                            case 0:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.First)); break;
                            case 1:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Second)); break;
                            case 2:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Third)); break;
                            case 3:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Fourth)); break;
                            case 4:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Fifth)); break;
                            case 5:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Sixth)); break;
                            case 6:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Seventh)); break;
                            case 7:
                                Tiles[x * _width + y] = new Tile(GetBool(Data, BitValues.Eighth)); count = -1; Data = Reader.ReadByte(); break;
                        }
                        count++;
                    }
                }
            }
        }

        internal bool GetBool(byte Store, BitValues Location)
        {
            return (Store & (byte)Location) == (byte)Location;
        }
    }
}
